﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace BitmapPtr
{
    class BitmapHelper
    {
        public static byte[] Bitmap2Byte(Bitmap bitmap, out int width, out int height,
            out int img_stride, out int img_depth, out PixelFormat img_PixelFormat)
        {
            byte[] destination;
            width = bitmap.Width;
            height = bitmap.Height;
            img_stride = 0;
            img_PixelFormat = bitmap.PixelFormat;
            img_depth = Bitmap.GetPixelFormatSize(img_PixelFormat);  //8,24,32

            try
            {
                Rectangle rect = new Rectangle(0, 0, width, height);
                BitmapData bitmapdata = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
                img_stride = bitmapdata.Stride;         //stride指图像一行的字节数(Width * depth + padding)
                int length = bitmapdata.Stride * bitmap.Height;
                destination = new byte[length];
                Marshal.Copy(bitmapdata.Scan0, destination, 0, length);
                bitmap.UnlockBits(bitmapdata);
            }
            catch
            {
                destination = null;
            }
            return destination;
        }
        public static Bitmap ByteToBitmap(byte[] img_bytes, int img_width, int img_height, PixelFormat PixelFormat)
        {
            try
            {
                Rectangle rect = new Rectangle(0, 0, img_width, img_height);
                Bitmap bitmap = new Bitmap(img_width, img_height, PixelFormat);
                BitmapData bitmapdata = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat);
                IntPtr destination = bitmapdata.Scan0;
                Marshal.Copy(img_bytes, 0, destination, bitmapdata.Stride * img_height);
                bitmap.UnlockBits(bitmapdata);
                return bitmap;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"ByteToBitmap error:{ex.Message}");
                return null;
            }
        }
        public static byte[] Color2GrayByte(byte[] bmpbype, int width, int height, int img_stride, int img_depth)
        {
            byte[] Graybytes = new byte[img_stride * height];   //Graybytes长度加一个字节可以避免pi指针在24位深度下的越界风险。
            int padding = img_stride - width * img_depth / 8;     //得到Padding，行末需要跳过去
            //灰度计算公式
            //Gray = (30 * Red + 59 *Green + 11 * Blue) / 100;
            //其中77,150,28分别除以256，即为上文的三个系数。
            //gray = (30 * p[0] + 59 * p[1] + 11 * p[2]) / 100;  //RGB

            unsafe
            {
                byte gray = 0;
                uint* pi;
                //https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/fixed-statement
                //在 fixed 语句中初始化的指针为只读变量。 如果想要修改指针值，必须声明第二个指针变量，并修改它。不能修改在 fixed 语句中声明的变量。
                fixed (byte* p0 = bmpbype)
                {
                    byte* p = p0;
                    fixed (byte* t0 = Graybytes)
                    {
                        byte* t = t0;
                        switch (img_depth)
                        {
                            case 32:          //32位深度，BGRA排列
                                for (int i = 0; i < height; i++)
                                {
                                    for (int j = 0; j < width; j++)
                                    {
                                        //gray = (byte)((77 * p[0] + 150 * p[1] + 28 * p[2]) >> 8); //最大值65025/255=255
                                        gray = (byte)((38 * p[0] + 75 * p[1] + 15 * p[2]) >> 7);    //最大值65025/255=255

                                        //t[0] = gray;t[1] = gray;t[2] = gray;t[3] = 255;           //Alpha通道填充成白色
                                        //使用int指针，一次写入四个字节。
                                        pi = (uint*)t;
                                        pi[0] = (uint)((gray) | (gray << 8) | (gray << 16) | 0xFF000000);       //argb

                                        //保留 t[0] = p[0];t[1] = p[1];t[2] = p[2];t[3] = 255;                   //Alpha通道填充成白色
                                        //保留 pi[0] = (uint)((p[0]) | (p[1] << 8) | (p[2] << 16) | 0xFF000000); //argb

                                        p += 4; t += 4;
                                    }
                                    if (padding > 0)
                                    {
                                        p += padding;
                                        t += padding;
                                    }
                                }
                                break;
                            case 24:          //24位深度，BGR排列
                                for (int i = 0; i < height; i++)
                                {
                                    for (int j = 0; j < width; j++)
                                    {
                                        gray = (byte)((38 * p[2] + 75 * p[1] + 15 * p[1]) >> 7);
                                        t[0] = gray; t[1] = gray; t[2] = gray;

                                        //Graybytes没有加一个字节，pi越界有风险。
                                        //pi = (uint*)t;
                                        //pi[0] = (uint)((gray) | (gray << 8) | (gray << 16));  

                                        p += 3;
                                        t += 3;
                                    }
                                    if (padding > 0)
                                    {
                                        p += padding;
                                        t += padding;
                                    }
                                }
                                break;

                            case 8:
                                for (int i = 0; i < height; i++)
                                {
                                    for (int j = 0; j < width; j++)
                                    {
                                        t[0] = p[0];
                                        p++; t++;
                                    }
                                    if (padding > 0)
                                    {
                                        p += padding;
                                        t += padding;
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            return Graybytes;
        }
    }
}
